
#pragma once

#include <assert.h>


class SoBase;
class SoNode;


template<typename T> class SoRef
{
private:
  mutable T* ptr_;

  mutable unsigned int  alive_;
  static const unsigned int ALIVE = 0xbeeffeed;
  static const unsigned int DEAD  = 0xdeadbeef;

  bool isOk() const {
    SoBase* bptr = ptr_; // Ensure implicit convertable to SoBase
    return
      (alive_ == ALIVE) &&
      (bptr  != nullptr) &&
      (bptr->getRefCount() > 0);
  }

  SoRef<T>(); // Ban default constructor since nullptr is banned

public:
  SoRef(T* ptr)              : ptr_(ptr), alive_(ALIVE) {
    ptr_ && ptr_->ref();  
    assert(isOk());
  }

  SoRef(SoRef<T> const& other) : ptr_(other.ptr_), alive_(ALIVE) { 
    assert(other.isOk());
    ptr_ && ptr_->ref();  
    assert(isOk());
  }

  ~SoRef() {
    assert(isOk());
    ptr_ && ptr_->unref();
    ptr_  = nullptr; 
    alive_ = DEAD;
  }

  SoRef<T>& operator = (SoRef<T> const& other) {
    assert(other.isOk());
    assert(isOk());
    dec();
    ptr_ = other.ptr_; 
    ptr_ && ptr_->ref(); 
    assert(isOk());
    return *this;
  }

  T* operator->() const {
    assert(isOk());
    return ptr_; 
  }

  T* get () const { 
    assert(isOk());
    T* n = const_cast<T*>(ptr_); // Thought this was not needed for mutable ptr_
    return n; 
  }

  T& operator*()const { 
    assert(isOk());
    return *ptr_; 
  }
  
  template<typename U>
  operator SoRef<U> () const { 
    assert(isOk());
    return SoRef<U>(ptr_); 
  } 

  operator SoRef<SoNode> () const { 
    assert(isOk());
    return SoRef<SoNode>(ptr_); 
  } 
};


template<typename T> SoRef<T>
  mkref() {
    return SoRef<T>(new T);
  }

template<typename T, typename A, typename... As> SoRef<T>
  mkref(A const& arg, As... args) {
    return SoRef<T>(new T(arg, args...));
  }


typedef SoRef<SoBase> SoRefb;
typedef SoRef<SoBase> const SoRefbc;

typedef SoRef<SoNode> SoRefn;
typedef SoRef<SoNode> const SoRefnc;